package com.woniu.service.impl;

import com.woniu.entity.Book;
import com.woniu.mapper.BookMapper;
import com.woniu.service.BookService;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;

@Service
//@AllArgsConstructor //生成全参的构造方法 1:set 2:属性 3:构造器(推荐)
public class BookServiceImpl implements BookService {
    @Resource
    private BookMapper bookMapper;
//    private StringRedisTemplate redisTemplate;
    @Autowired
    private RedisTemplate<String,Object> redisTemplateInit;
    ReentrantLock reentrantLock = new ReentrantLock();
    @Override
    public List<Book> queryAll() {
        return bookMapper.queryAll();
    }

    /**
     * 给接口数据做缓存
     * 有三大问题
     * 缓存穿透，如果id不存在，还是会多次访问数据库
     *      解决方案
     *      第一种：缓存空对象 book == null?new Book():book
     *      缺点：保存很多垃圾数据（浪费内存【致命！！】），优点：简单
     *      第二种：很高级,使用布隆过滤器来解决缓存穿透
     *      布隆过滤器：底层采用redis的bitmap（很长的二进制数组）数据类型实现
     *      缺点：会出现误判
     *      优点:用布隆过滤器判断该数据是创存在，只用了一个bit标识，节约内存空间
     *
     * 缓存雪崩：
     * 1.同一时间大量缓存的key失效，
     * 或者，
     * 2.redis宕机，导致大量请求到达数据库
     *      解决方案
     *      1.给key加上不同的过期时间（随机数）
     *      2.1利用redis集群（多设置几个）提高服务可有性
     *      2.2给业务添加多级缓存
     *      ·本地缓存 Caffeine
     *      ·分布式缓存（利用redis缓存）
     * 缓存击穿：热点key问题，就是高并发key突然失效，
     *      解决方案：加锁
     *
     *
     * redis五种基本类型String,hash,set,zset,list
     * String:基本存储类型，key-val key会默认最小占512字节。有些key远远没有512字节，就会浪费内存
     *
     * @param id
     * @return
     */
    @Override
    public Book queryBookById(Integer id) {

        try {
            //查询redis中是否有对应的缓存数据
            Book cacheBook =(Book) redisTemplateInit.opsForHash().get("CACHE:BookServiceImpl:queryBookById",id+"");
            if (cacheBook !=null){
                return cacheBook;
            }

            boolean b = reentrantLock.tryLock();
            if (b){
                Book book = bookMapper.queryBookById(id);
                //把book数据存入redis
                redisTemplateInit.opsForHash().put("CACHE:BookServiceImpl:queryBookById",id+"",book == null ? new Book() : book);
                return book;
            }else {
                return null;
            }

        } finally {
            if (reentrantLock.isHeldByCurrentThread() && reentrantLock.isLocked()){
                reentrantLock.unlock();
            }
        }
    }
}
